package com.course.server.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import com.alibaba.fastjson.JSON;
import com.course.server.common.Constant;
import com.course.server.dto.LoginUser;
import com.course.server.dto.PageDto;
import com.course.server.dto.ResourceDto;
import com.course.server.dto.UserDto;
import com.course.server.entity.RoleUser;
import com.course.server.entity.RoleUserExample;
import com.course.server.entity.User;
import com.course.server.entity.UserExample;
import com.course.server.enums.BusinessExceptionEnum;
import com.course.server.exception.UserException;
import com.course.server.mapper.MyUserMapper;
import com.course.server.mapper.RoleUserMapper;
import com.course.server.mapper.UserMapper;
import com.course.server.util.CopyUtil;
import com.course.server.util.UuidUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author: Durian
 * @date: 2021/5/30 13:06
 */
@Slf4j
@Service
public class UserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private RoleUserMapper roleUserMapper;
    @Resource
    private MyUserMapper myUserMapper;

    public PageDto<UserDto> list(int page, int size) {
        PageHelper.startPage(page, size);
        UserExample example = new UserExample();
        // service层尽量灵活
        List<User> users = userMapper.selectByExample(example);
        PageInfo<User> pageInfo = new PageInfo<>(users);

        List<UserDto> userDtos = CopyUtil.copyList(users, UserDto.class);
        PageDto<UserDto> pageDto = new PageDto<>();
        pageDto.setPage(page);
        pageDto.setSize(size);
        pageDto.setList(userDtos);
        pageDto.setTotal(pageInfo.getTotal());
        return pageDto;
    }

    public List<UserDto> listAll() {
        UserExample example = new UserExample();
        return CopyUtil.copyList(userMapper.selectByExample(example), UserDto.class);
    }

    public void save(UserDto userDto) {
        User user = new User();
        BeanUtils.copyProperties(userDto, user);
        if (StrUtil.isBlank(userDto.getId())) {
            if (!CollectionUtils.isEmpty(findByName(user.getLoginName()))) {
                throw new UserException(BusinessExceptionEnum.USER_ALREADY_EXISTS);
            }
            insert(user);
        } else {
            update(user);
        }
    }

    public void delete(String id) {
        userMapper.deleteByPrimaryKey(id);
    }

    private void insert(User user) {
        Digester sha = new Digester(DigestAlgorithm.SHA256);
        String shaPassword = sha.digestHex(user.getLoginName() + user.getPassword() + Constant.SALT);
        user.setId(UuidUtils.getShortUuid());
        user.setPassword(shaPassword);
        userMapper.insert(user);
    }

    private void update(User user) {
        // 登录名不能修改，防止前端恶意传入
        user.setLoginName(null);
        userMapper.updateByPrimaryKeySelective(user);
    }

    // 登录名不能重复
    private List<User> findByName(String name) {
        UserExample example = new UserExample();
        example.createCriteria().andLoginNameEqualTo(name);
        return userMapper.selectByExample(example);
    }

    public void updatePass(UserDto userDto) {
        User user = new User();
        BeanUtils.copyProperties(userDto, user);
        Digester sha = new Digester(DigestAlgorithm.SHA256);
        String shaPassword = sha.digestHex(user.getLoginName() + user.getPassword() + Constant.SALT);
        user.setPassword(shaPassword);
        userMapper.updateByPrimaryKeySelective(user);
    }

    public LoginUser login(UserDto userDto) {
        Digester sha = new Digester(DigestAlgorithm.SHA256);
        String shaPassword = sha.digestHex(userDto.getLoginName() + userDto.getPassword() + Constant.SALT);
        List<User> users = findByName(userDto.getLoginName());
        if (CollectionUtils.isEmpty(users)) {
            throw new UserException(BusinessExceptionEnum.USER_NOT_EXISTS);
        }
        User user = users.get(0);
        if (user == null || !Objects.equals(shaPassword, user.getPassword())) {
            throw new UserException(BusinessExceptionEnum.PASSWORD_ERROR);
        }
        LoginUser loginUser = CopyUtil.copy(user, LoginUser.class);
        // 登录成功 设置权限
        setAuth(loginUser);
        return loginUser;
    }

    public List<String> getRole(String userId) {
        RoleUserExample example = new RoleUserExample();
        example.createCriteria().andUserIdEqualTo(userId);
        List<RoleUser> roleUsers = roleUserMapper.selectByExample(example);
        return roleUsers.stream().map(RoleUser::getRoleId).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveRole(UserDto userDto) {
        String userId = userDto.getId();
        List<String> roleIds = userDto.getRoleIds();
        RoleUserExample example = new RoleUserExample();
        example.createCriteria().andUserIdEqualTo(userId);
        // 删除原记录
        roleUserMapper.deleteByExample(example);
        // 保存角色
        roleIds.forEach(id -> {
            RoleUser roleUser = new RoleUser();
            roleUser.setId(UuidUtils.getShortUuid());
            roleUser.setRoleId(id);
            roleUser.setUserId(userId);
            roleUserMapper.insert(roleUser); // todo 优化批量插入
        });
    }

    public List<ResourceDto> getResources(String userId) {
        return myUserMapper.getResource(userId);
    }

    private void setAuth(LoginUser loginUser) {
        List<ResourceDto> resourceDtos = myUserMapper.getResource(loginUser.getId());
        // 提取接口请求权限
        HashSet<String> requests = new HashSet<>();
        if (CollectionUtil.isNotEmpty(resourceDtos)) {
            resourceDtos.stream().map(resource -> {
                String requestStr = resource.getRequest();
                return JSON.parseArray(requestStr, String.class);
            }).filter(CollectionUtil::isNotEmpty).forEach(requests::addAll);
        }
        loginUser.setResources(resourceDtos);
        loginUser.setRequests(requests);
        log.info("用户[{}]的resources=[{}], requests=[{}]", loginUser.getLoginName(), resourceDtos, requests);
    }
}
